module ActiveBackground
  class Manager
    include Singleton
    
    @@config = {
                :sleep => 10
              }
    
    def initialize
      @jobs = Hash.new
      @queue = Hash.new
      @running = true
      @lock = Monitor.new
      @threads = Hash.new
      @terminated = nil
      run
    end
    
    def config=(config)
      @@config.merge!(config)
    end
    
    def run
      begin
        @thread = Thread.new do
          while @running
            ACTIVE_BACKGROUND_LOGGER.info "busy... with #{@jobs.length} jobs"
            process
          end
        end
      rescue Exception => e
        ACTIVE_BACKGROUND_LOGGER.warn(e.to_s + "\n ---Brackpoint--- \n" + e.backtrace.join("\n"))
        run
      end
    end

    def process
      unless @jobs.empty?
        @jobs.each do |key, job|
          unless @terminated
            if job.status == :created
              @threads[job.key] = Thread.new do
                ACTIVE_BACKGROUND_LOGGER.info 'process job with key ' + key.to_s
                job.process()
              end
            else
              @lock.synchronize do
                remove_job(job.key) if job.terminated?
              end
            end
          end
          ACTIVE_BACKGROUND_LOGGER.info "#{@jobs.length} jobs in queue"
        end
      end
      @lock.synchronize do
        @jobs.merge!(@queue)
        @queue=Hash.new
      end
      sleep @@config[:sleep] unless @terminated
      ACTIVE_BACKGROUND_LOGGER.info "#{@jobs.length} jobs in queue"
      GC.start
    end

    def stop
      @running = false
    end

    def terminate
      unless @terminated and !@running
        @lock.synchronize do
          @terminated = true
          @running = false
        end
      end
    end

    def terminated?
      @lock.synchronize do
        @terminated
      end
    end

    def alive?
      true
    end

    def send_command_to_job(key, method, *args)
      begin
        @lock.synchronize do
          unless args.empty?
            return @jobs[key].send(method, args) if @jobs[key]
          else
            return @jobs[key].send(method) if @jobs[key]
          end
        end
      rescue Exception => e
        ACTIVE_BACKGROUND_LOGGER.fatal(e.to_s + "\n" + e.backtrace.join("\n"))
      end
    end

    def wait
      @thread.join
    end

    def get_jobs
      @lock.synchronize do
        if block_given?
          yield @jobs
        else
          return @jobs
        end
      end
    end
    
    def each_job
      @lock.synchronize do
        @jobs.each do |key, job|
          if block_given?
            yield key, job
          else
            # TODO: raise error
          end
        end
      end
    end
    
    def get_job(key)
      @lock.synchronize do
        @jobs[key]
      end
    end
        
    def add_job(key, job)
      @lock.synchronize do
        @queue[key] = job
        @threads[key] = nil
      end
    end
    
    private
      def remove_job(key)
        @lock.synchronize do
          @jobs.delete(key)
          @threads[key].kill if @threads[key]
          @threads.delete(key)
        end
      end
  end
end